<?php
/**
 * Élément de base des objets
 *
 * @package cass
 * @author Mickaël CASTANHEIRO <mickael.castanheiro@gmail.com>
 * @version 1.0
 */
namespace cass;
require_once __PATH__ . '/lib/cass/abstract/Singleton.class.php';

class FrontController extends Singleton {

    protected $parameters;
    protected $uri;

    /**
     * Constructeur du frontController
     */
    protected function __construct() {
        require_once __PATH__ . '/lib/cass/Data.class.php';

        // Fonctions helper
        require_once __PATH__ . Data::directories('cass') . 'functions/helper.lib.php';

        // fonctions sur les strings
        require_once __PATH__ . Data::directories('cass') . 'functions/string.lib.php';

        // fonctions sur les strings
        require_once __PATH__ . Data::directories('cass') . 'functions/array.lib.php';

        // Assigne l'autoloader
        spl_autoload_register(__NAMESPACE__ . '\FrontController::autoloader');

        // Assigne le gestionnaire d'erreurs
        set_exception_handler(array(__NAMESPACE__ . '\FrontController', 'exceptioner'));

        $this->parameters = new Parameters;
        $this->uri = new Uri;
    }

    /**
     * Charge automatiquement les classes demandées
     * @param <string> $_className Nom de la classe à charger
     * @param <boolean> La classe a été trouvée et chargée
     */
    static public function autoloader($_className) {
        $_className = substr($_className, strrpos($_className, '\\') + 1);
        if (self::autoloaderRecursive($_className, __PATH__ . Data::directories('cass')))
            return true;
        return false;
    }

    /**
     * Charge automatiquement les classes demandées récursivement
     * @param <string> $_className Nom de la classe à charger
     * @param <string> _dirName Répertoire où chercher
     * @param <boolean> La classe a été trouvée et chargée
     */
    static public function autoloaderRecursive($_className, $_dirName) {
        $fileName = $_dirName . $_className . Data::extensions('class');
        if (file_exists($fileName)) {
            require_once $fileName;
            return true;
        }
        $directories = scandir($_dirName);
        foreach ($directories AS $fileName) {
            if (($fileName == '.') OR ($fileName == '..'))
                continue;

            $fileName = $_dirName . $fileName;

            if (!is_dir($fileName))
                continue;

            if (static::autoloaderRecursive($_className, $fileName . '/')) {
                return true;
            }
        }
        return false;
    }

    /**
     * Gère les exceptions
     * @param $_error Erreur transmise
     */
    static public function exceptioner(\Exception $_error) {      
        switch (get_class($_error)) {
            case 'cass\error404Exception':
                static::getInstance()->render(Uri::create('@404'));
                break;
            case 'cass\forwardException':
                static::getInstance()->render($_error->getUri());
                break;

            default:
                var_dump($_error);
        }
    }

    /*
     * Éxécute l'action réclammée
     * et envoie la requête au rendu complet 
     */

    public function execute() {
        $uri = Uri::createFromURI($_SERVER['REQUEST_URI']);
        $this->uri = $uri;
        $this->render($uri, false);
    }

    /**
     * Donne un rendu partiel (sans la structure) d'une action
     * @param <string> $_package Package
     * @param <string> $_action Action
     * @param <string> $_request Requête
     * @param <array> $_parameters Paramètres
     */
    public function render(Uri $_uri, $_partialRender=false) {
        $actionController = ActionController::getInstance();
        $viewController = ViewController::getInstance();

        // Pour savoir si le cache est à mettre à jour
        $cacheFinded = false;
        $saveCache = false;

        // Vérifie qu'un cache n'existe pas
        if (APPLICATION_ENV != 'dev') {
            $cache = new Cache;
            $cache->setType($actionController->getCache($_uri));
            $cache->setUri($_uri);

            // Vérifie que le cache soit à gérer
            if ($cache->isEnabled()) {
                $render = $cache->getRender();

                // S'il existe un retour, on ajoute les paramètres
                if ($render !== false) {
                    $this->parameters->merge($cache->getParameters());
                    $cacheFinded = true;
                }
                else {
                    $saveCache = true;
                }
            }
        }

        // S'il faut sauver le cache ou qu'on est en mode dev, exécuter l'action
        if (!$cacheFinded OR (APPLICATION_ENV == 'dev')) {
            $parameters = $actionController->execute($_uri);
            $this->parameters->merge($parameters); // Fusionne les paramètres de l'action et ceux du contexte de la page
            $render = $viewController->getRender($_uri, $this->parameters);

            // Sauve le cache dans un fichier
            if ($saveCache) {
                $cache->getParameters()->merge($parameters); // Ajoute les paramètres de l'action uniquement
                $cache->setRender($render);
            }
        }

        // Affiche le résultat de l'action
        if ($_partialRender) {
            $viewController->render($render);
        }
        else {
            $viewController->renderWithStructure($render, ($_uri->package == 'admin' ) ? Data::actions('structureAdminTemplate') : Data::actions('structureTemplate'), $this->parameters);
        }
    }

    /**
     * Transmet la page
     */
    public function forward() {
        $uri = call_user_func_array(array('\cass\Uri', 'create'), func_get_args());
        throw new forwardException($uri);
    }

    /**
     * Redirige vers une action
     */
    public function redirect() {
        $uri = call_user_func_array(array('\cass\Uri', 'create'), func_get_args());
        $this->redirectUrl(Data::directories('uri') . $uri->getLink());
    }

    /**
     * Redirige la page vers une URL
     * @param <string> $_url URL relative de redirection
     */
    public function redirectUrl($_url) {
        if ( empty($_url) ) {
            $_url = Data::directories('uri');
        }
        header('Location: ' . $_url);
        exit;
    }

}
